package com.example.app.aspect;

import com.example.app.annotation.RedisRateLimiter;
import lombok.AllArgsConstructor;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.redisson.api.RRateLimiter;
import org.redisson.api.RateIntervalUnit;
import org.redisson.api.RateType;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;

@Aspect
@Component
@AllArgsConstructor
public class RedisRateLimiterAspect {
    private final RedissonClient redissonClient;

    @Around("@within(com.example.app.annotation.RedisRateLimiter) || @annotation(com.example.app.annotation.RedisRateLimiter)")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        // 获取方法签名
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        // 获取注解（优先方法级，再类级）
        RedisRateLimiter rateLimiter = method.getAnnotation(RedisRateLimiter.class);
        if (rateLimiter == null) {
            rateLimiter = joinPoint.getTarget().getClass().getAnnotation(RedisRateLimiter.class);
        }
        // 生成限流 key
        String key = generateKey(rateLimiter, joinPoint);
        // 获取或创建限流器
        RRateLimiter limiter = redissonClient.getRateLimiter(key);
        // 设置限流规则（仅首次执行，后续复用配置）
        if (!limiter.isExists()) {
            limiter.trySetRate(
                    RateType.OVERALL,
                    rateLimiter.rate(),
                    1,
                    RateIntervalUnit.SECONDS
            );
        }
        // 尝试获取令牌
        boolean acquired = limiter.tryAcquire(1, rateLimiter.waitTime(), java.util.concurrent.TimeUnit.MILLISECONDS);
        if (!acquired) {
            throw new RuntimeException(rateLimiter.message());
        }
        // 执行业务逻辑
        return joinPoint.proceed();
    }

    private String generateKey(RedisRateLimiter annotation, ProceedingJoinPoint joinPoint) {
        if (!"".equals(annotation.keyPrefix())) {
            return "rate_limit:" + annotation.keyPrefix();
        }
        // 默认使用 类名+方法名
        return "rate_limit:" +
                joinPoint.getTarget().getClass().getSimpleName() + "." +
                joinPoint.getSignature().getName();
    }
}
